#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/wait.h>
#include<linux/poll.h>
#include<linux/sched.h>
#include<linux/irq.h>
#include<linux/interrupt.h>
#include<linux/slab.h>
#include<mach/gpio.h>
#include<mach/regs-gpio.h>
#include<plat/gpio-cfg.h>

#define DEVICE_NAME "buttons"

struct button_desc{
    int gpio;
    int number;
    char *name;
};

static struct button_desc buttons[] = {
    {S5PV210_GPH2(0),0,"KEY0"},
    {S5PV210_GPH2(1),1,"KEY1"},
    {S5PV210_GPH2(2),2,"KEY2"},
    {S5PV210_GPH2(3),3,"KEY3"},
    {S5PV210_GPH3(0),4,"KEY4"},
    {S5PV210_GPH3(1),5,"KEY5"},
    {S5PV210_GPH3(2),6,"KEY6"},
    {S5PV210_GPH3(3),7,"KEY7"},
};

#define OK (0)
#define ERROR (-1)

struct cdev *gDev;
struct file_operations *gFile;
dev_t devNum;
unsigned int subDevNum = 1;
int reg_major = 232;
int reg_minor = 0;

static irqreturn_t button_interrupt(int irq,void *dev_id)
{
    struct button_desc *bdata = (struct button_desc *)dev_id;	
    unsigned tmp;
    
    tmp = gpio_get_value(bdata->gpio); //获取gpio的电平
    
    /*active low*/	
    printk("KEY:%d;%04x\n",bdata->number,tmp);
    
    return IRQ_HANDLED;
}

int butsOpen(struct inode *p,struct file *f)
{
    int irq;
    int i;
    int err = 0;
    
    printk(KERN_EMERG"butsOpen\r\n");
    
    for(i = 0;i < ARRAY_SIZE(buttons);i++)
    {
        if(!buttons[i].gpio)continue;
        
        irq = gpio_to_irq(buttons[i].gpio);//获取中断号  函数定义在gpio.h里边
        
        //原型
        //request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char * name,void * dev)
        err = request_irq(irq,button_interrupt,IRQ_TYPE_EDGE_BOTH,buttons[i].name,(void *)&buttons[i]);
        
        if(err) break;
    }
	
    if(err)
    {
        i--;
        
        for(;i>=0;i--)
        {
            if(!buttons[i].gpio)continue;
            
            irq = gpio_to_irq(buttons[i].gpio);
            
            disable_irq(irq);
            
            free_irq(irq,(void *)&buttons[i]);
        }
        
        return -EBUSY;
    }
    
    return 0;
}

int charDrvInit(void)
{
    devNum = MKDEV(reg_major,reg_minor);
    
    printk(KERN_EMERG"devNum is %d\r\n",devNum);
    
    if(OK == register_chrdev_region(devNum,subDevNum,DEVICE_NAME))
    {
    	printk(KERN_EMERG"register_chrdev_region ok\r\n");
    }
    else
    {
    	printk(KERN_EMERG"register_chrdev_region error\r\n");	
    }
    
    gDev = kzalloc(sizeof(struct cdev),GFP_KERNEL);
    
    gFile = kzalloc(sizeof(struct file_operations),GFP_KERNEL);
    
    gFile->open = butsOpen;
    
    gFile->owner = THIS_MODULE;
    
    cdev_init(gDev,gFile);
    
    cdev_add(gDev,devNum,3);
    
    printk(KERN_EMERG"button driver initial done..\r\n");
    
    return 0;	
}

void __exit charDrvExit(void)
{
    int i = ARRAY_SIZE(buttons);
    int irq;
    
    while(i--)
    {
        if(!buttons[i].gpio)continue;
        
        irq = gpio_to_irq(buttons[i].gpio);
        
        disable_irq(irq);
        
        free_irq(irq,(void *)&buttons[i]);  	
    }
    
    cdev_del(gDev);
    
    unregister_chrdev_region(devNum,subDevNum);
    
    return ;
}

module_init(charDrvInit);
module_exit(charDrvExit);
MODULE_LICENSE("GPL");